#include "Qt3DPicker.h"

#include "OsgUtil.h"

#include <TopExp.hxx>
#include <TopoDS.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepOffsetAPI_MakePipeShell.hxx>
#include <Geom2d_Curve.hxx>
#include <Geom_Curve.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Wire.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Face.hxx>
#include <TopExp_Explorer.hxx>
#include <BRepAlgoAPI_Check.hxx>
#include <gp_Ax1.hxx>
#include <gp_Ax3.hxx>

#include "SweepHandler.h"
#include "CmdManager.h"
#include "Scene.h"
#include "SendPointHandlerBase.h"
#include "SelectHandler3D.h"
#include "OSGNodeMask.h"
#include "Operation/OpAdd.h"
#include "QtOSGWidget.h"
#include "Surface.h"
#include "Pline2D.h"
#include "EntFromEdge.h"

SweepHandler::SweepHandler()
{
}

SweepHandler::~SweepHandler()
{
}

bool getStartDir(const  std::shared_ptr<Ent>& ent, osg::Vec3d& startPt, osg::Vec3d& startDir)
{
	auto ent2d = dynamic_cast<Ent2DCurve*>(ent.get());
	if (ent2d)
	{
		auto curve2ds = ent2d->getCurve2d();
		if (curve2ds.empty())
			return false;
		auto curve2d = curve2ds[0];
		gp_Pnt2d startP;
		gp_Vec2d startV;
		curve2d->D1(curve2d->FirstParameter(), startP, startV);
		UCS* ucs = dynamic_cast<UCS*>(ent->getParent());
		if (!ucs)
			return false;
		auto matrix = ucs->getMatrix();
		startPt = { double(startP.X()), double(startP.Y()),0 };
		startDir = { double(startV.X()), double(startV.Y()), 0 };
		startPt = startPt * matrix;
		// 方向变换，不是点变换，所以去掉平移分量。注意transform3x3参数的顺序
		startDir = osg::Matrixd::transform3x3(startDir, matrix);
	}
	else
	{
		auto ent3d = dynamic_cast<Ent3DCurve*>(ent.get());
		auto curve3ds = ent3d->getCurve3d();
		if (curve3ds.empty())
			return false;
		auto curve3d = curve3ds[0];
		gp_Pnt startP;
		gp_Vec startV;
		curve3d->D1(curve3d->FirstParameter(), startP, startV);
		startPt = { double(startP.X()), double(startP.Y()), double(startP.Z()) };
		startDir = { double(startV.X()), double(startV.Y()), double(startV.Z()) };
	}
	return true;
}

bool isSmoothCurve(const std::shared_ptr<Pline2D>& pline)
{
	auto curve2ds = pline->getCurve2d();
	if (curve2ds.empty())
		return false;
	for (int i = 0; i<int(curve2ds.size() - 1); i++)
	{
		auto first2d = curve2ds[i];
		gp_Pnt2d pt;
		gp_Vec2d firstV;
		first2d->D1(first2d->LastParameter(), pt, firstV);
		firstV.Normalize();
		auto second2d = curve2ds[i+1];
		gp_Vec2d secondV;
		second2d->D1(second2d->FirstParameter(), pt, secondV);
		secondV.Normalize();
		bool equal = firstV.IsEqual(secondV, 1E-7, 1E-7);
		return equal;
	}
	return false;
}

void SweepHandler::input(const QVariant& data, const QString& rawInput, CommandSource cs)
{
	auto scene = getScene();
	auto sph = getSendPointHandler();
	auto selector = qobject_cast<SelectHandler3D*>(getSelectHandler());
	if (data.type() == QVariant::Bool)
	{
		bool esc = !data.toBool();
		if (esc)
		{
			selector->clearSelection();
			clearInput();
			emit finished();
			return;
		}
	}

	if (status() == 0)
	{
		if (selector)
		{
			selector->clearSelection();
			selector->beginSelect(D2LineMask | D2CurveMask);
			auto picker = getPicker();
			m_oldPickMode = picker->getPickMode();
			picker->setPickMode(BOX_MODE);
			prompt(1, tr("选择轮廓线:"));
		}
		else
		{
			emit finished();
		}
		return;
	}


	if (status() == 1)
	{
		if (selector->done())
		{
			auto picker = getPicker();
			picker->setPickMode(m_oldPickMode);
			std::set<std::shared_ptr<INode>> selections = selector->selectionSet();
			if (selections.empty())
			{
				emit finished();
				return;
			}
			for (auto selection : selections)
			{
				auto ent = std::dynamic_pointer_cast<Ent>(selection);
				auto parent = ent->getParent();
				UCS* ucs = dynamic_cast<UCS*>(parent);
				if (ucs && !m_ucs)
				{
					m_ucs = ucs;
				}
				if(ucs != m_ucs)
				{
					remark(tr("请选择同一平面内的图形"));
					emit finished();
					return;
				}
				m_profiles.push_back(ent);
			}
			selector->clearSelection();
			selector->beginSelect(D2LineMask | D2CurveMask | D3LineMask | D3CurveMask, 1);
			prompt(2, tr("选择扫掠轨迹:"));
		}		
		return;
	}

	if (status() == 2)
	{
		if (selector->done())
		{
			std::set<std::shared_ptr<INode>> selections = selector->selectionSet();
			if (selections.empty())
				return;
			m_spine = std::dynamic_pointer_cast<Ent>(*selections.begin());
			if (m_spine->metaObject() == &Pline2D::staticMetaObject)
			{
				auto pline = std::dynamic_pointer_cast<Pline2D>(m_spine);
				bool isSmooth = isSmoothCurve(pline);
				if (!isSmooth)
				{
					remark(tr("轨迹不是圆滑曲线"));
					emit finished();
					return;
				}
			}
			if (sph)
			{
				sph->setPointMask(CS_LBTNDOWN | CS_MOUSEMOVE);
			}
			enableMouseHandler(MouseHandlerID::PointSender);
			prompt(3, tr("指定基点:"));
		}
		return;
	}
	if (status() == 3)
	{
		if (data.type() == QVariant::Vector3D)
		{
			if (cs == CS_MOUSEMOVE)
				return;
			auto pt = data.value<QVector3D>();
			osg::Vec3d basePt = { pt.x(), pt.y(), pt.z() };
			auto spineEdges = makeEdges(m_spine);
			//BRepBuilderAPI_MakeWire makeSpineWire;
			BRepLib_MakeWire makeSpineWire;
			for (auto spineEdge : spineEdges)
			{
				makeSpineWire.Add(spineEdge);
			}
			if (!makeSpineWire.IsDone())
			{
				remark(tr("构造扫掠线Wire失败"));
				emit finished();
				return;
			}
			TopoDS_Wire spineWire = makeSpineWire.Wire();
			std::set<std::shared_ptr<INode>> result;
			selector->clearSelection();
			osg::Vec3d startPt, startDir;
			bool success = getStartDir(m_spine, startPt, startDir);
			if (!success)
			{
				remark(tr("获取扫掠线起始点和起始方向失败"));
				emit finished();
				return;
			}
			osg::Vec3d dir = startDir;
			auto zAxis = m_ucs->zAxis();
			gp_Trsf trsf;
			if (std::abs(osg::Vec3d(dir) * zAxis) > 0.99999847691328769880290124792571/*cos(0.1deg)*/)
			{
				trsf.SetTranslation(gp_Pnt(basePt.x(), basePt.y(), basePt.z()), gp_Pnt(startPt.x(), startPt.y(), startPt.z()));
			}
			else
			{
				auto rotateAxisVec = zAxis ^ dir;
				rotateAxisVec.normalize();
				gp_Ax1 fromAx1(gp_Pnt(), gp_Dir(zAxis.x(), zAxis.y(), zAxis.z()));
				gp_Ax1 toAx1(gp_Pnt(), gp_Dir(dir.x(), dir.y(), dir.z()));
				double angle = fromAx1.Angle(toAx1);
				if (angle > M_PI)
					rotateAxisVec = -rotateAxisVec;
				gp_Trsf trsfMove, trsfRot;
				trsfMove.SetTranslation(gp_Pnt(basePt.x(), basePt.y(), basePt.z()), gp_Pnt(startPt.x(), startPt.y(), startPt.z()));
				gp_Ax1 rotateAx1(gp_Pnt(basePt.x(), basePt.y(), basePt.z()), gp_Dir(rotateAxisVec.x(), rotateAxisVec.y(), rotateAxisVec.z()));
				trsfRot.SetRotation(rotateAx1, angle);
				trsf = trsfMove * trsfRot;
			}
			TopLoc_Location loc(trsf);
			for (auto profile : m_profiles)
			{
				BRepOffsetAPI_MakePipeShell makePipe(spineWire);		
				auto clone = profile->clone();
				auto profileEdges = makeEdges(clone);
				BRepBuilderAPI_MakeWire makeWire;
				for (auto profileEdge : profileEdges)
				{
					makeWire.Add(profileEdge);
				}
				if (!makeWire.IsDone())
				{
					remark(tr("构造扫掠线Wire失败"));
					emit finished();
					return;
				}
				TopoDS_Wire profileWire = makeWire.Wire();
				TopoDS_Shape profileShap = profileWire.Moved(loc);
				makePipe.Add(profileShap, Standard_False, Standard_False);
				makePipe.SetTransitionMode(BRepBuilderAPI_TransitionMode::BRepBuilderAPI_Transformed);
				makePipe.SetMode(true);
				makePipe.Build();
				TopoDS_Shape shape;
				if (makePipe.IsDone())
				{
					shape = makePipe.Shape();
				}
				BRepAlgoAPI_Check check(shape);
				if (!check.IsValid())
				{
					remark(tr("生成图形不合格"));
					emit finished();
					return;
				}
				TopExp_Explorer Ex;
				for (Ex.Init(shape, TopAbs_FACE); Ex.More(); Ex.Next())
				{
					TopoDS_Face face = TopoDS::Face(Ex.Current());
					auto surface = std::make_shared<Surface>(scene);
					surface->setShape(face);
					result.insert(surface);
				}
			}
			auto undoStack = scene->undoStack();
			undoStack->push(new OpAdd(result));
			emit finished();
		}
	}
}
